/*
Copyright (c) [2019年5月1日] [吴超]
[MBT_studio] is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
		 http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
*/
#include "stdafx.h"

#include "场景.h"
#include "面/面.h"
#include "core/引擎配置.h"



static std::map<uint32, S_Scene*> g场景容器;
static C_Key分配器                g场景ID分配器;




S_Scene* f_scene_创建场景(uint32 key) {
	key = g场景ID分配器.f_Gen();
	S_Scene* scene = new S_Scene(key);
	
	g场景容器[key] = (scene);
	return scene;
}

void f_scene_销毁(uint32 key) {
	if (g场景容器.find(key) != g场景容器.end()) {
		delete g场景容器[key];

		g场景ID分配器.f_Move(key);
		g场景容器.erase(key);
	}
}

S_Scene* f_scene_get场景(uint32 key) {
	if (g场景容器.find(key) != g场景容器.end()) {
		return g场景容器[key];
	}
	return nullptr;
}

void f_scene_渲染(S_Scene* scene) {

}

void f_scene_添加物体列表(S_Scene* scene, S_物体* ob) {
	switch (ob->m_Type) {
	case E_物体类型::t_多边形:
	case E_物体类型::t_网格:
	case E_物体类型::t_空:
		scene->m_物体_向全局内存过渡.push_back(ob);

		ob->m_isUpdate = true;
		ob->m_UpdateGPU参数布局 = true;
	default:
		break;
	}
}

void f_scene_场景链接物体(uint32 sceneID, uint32 objectID) {

}

void f_scene_场景链接物体(S_Scene* scene, S_物体* object) {
	scene->m_物体.push_back(object);
}

void f_scene_开启光追(S_Scene* scene, S_设备环境& ctx) {
	
	S_线管创建配置 图元配置 = {
			{
				{0, E_着色阶段::e_光追射线生成 | E_着色阶段::e_光追最近相交, E_板载缓存类型::e_光追BVH},

				{1, E_着色阶段::e_光追射线生成, E_板载缓存类型::e_IMAGE},

				{2, E_着色阶段::e_光追射线生成 | E_着色阶段::e_光追最近相交 | E_着色阶段::e_光追忽略相交, E_板载缓存类型::e_UBO},

				{DEF_ObMeshBindID, E_着色阶段::e_光追最近相交 | E_着色阶段::e_光追回调, E_板载缓存类型::e_SSBO},

				{DEF_MaterialsBindID, E_着色阶段::e_光追最近相交 | E_着色阶段::e_光追回调 | E_着色阶段::e_光追忽略相交, E_板载缓存类型::e_SSBO},

				{DEF_TexturesBindID, E_着色阶段::e_光追最近相交 | E_着色阶段::e_光追回调, E_板载缓存类型::e_SAMPLER_Array},

				{DEF_RenderFrameBindID, E_着色阶段::e_光追射线生成 | E_着色阶段::e_光追最近相交 | E_着色阶段::e_光追回调 | E_着色阶段::e_光追忽略相交, E_板载缓存类型::e_UBO},

				{DEF_ParallelLightBindID, E_着色阶段::e_光追最近相交 | E_着色阶段::e_光追回调 | E_着色阶段::e_光追忽略相交, E_板载缓存类型::e_SSBO},

				{DEF_BINDING_Render_Geom, E_着色阶段::e_光追最近相交 | E_着色阶段::e_光追回调 | E_着色阶段::e_光追忽略相交, E_板载缓存类型::e_SSBO},

				{DEF_BINDING_Render_GlobalParam, E_着色阶段::e_光追最近相交 | E_着色阶段::e_光追回调 | E_着色阶段::e_光追忽略相交, E_板载缓存类型::e_SSBO},
			},
			{
			},
			{
				{S_引擎配置::m_3D着色器文件根路径 + "物理光照_光线生成.spv", E_着色阶段::e_光追射线生成},
				{S_引擎配置::m_3D着色器文件根路径 + "物理光追_光线忽略.spv", E_着色阶段::e_光追忽略相交},
				//{"D:/U/Project/engine/材质/着色/compiled/物理光照_最近相交.spv", E_着色阶段::e_光追最近相交},
				{S_引擎配置::m_3D着色器文件根路径 + "物理辐射光照_最近相交.spv", E_着色阶段::e_光追最近相交},
				//{"D:/U/Project/engine/材质/着色/compiled/PBR_光线回调.spv ", E_着色阶段::e_光追回调},
			}
	};

	scene->m_光追后端 = f_df_create光追后端(ctx, 图元配置);

	f_allocGPU参数缓存槽(&scene->m_光追后端->m_参数布局, 图元配置.m_参数布局.size());


	scene->m_光追顶层加速结构 = f_buf_alloc(S_Mesh::g_TLAS, 0);
	scene->m_光追网格对象 = f_buf_alloc(S_Mesh::g_RtMesh, 0);
}

void f_scene_销毁光追渲染器(S_Scene* scene) {
	f_df_销毁光追后端(scene->m_光追后端);

	//f_buf_erase(scene->m_光追顶层加速结构);
}

void f_scene_重构光追线管(S_Scene* scene, S_设备环境& ctx) {

}

void f_scene_添加光追材质着色器(S_Scene* scene, const std::string path, int32 loc) {
	if (loc >= 0 && loc + E_光追阶段ID::e_光追阶段ID_材质回调 < scene->m_光追后端->m_线管创建配置信息.m_Paths.size()) {
		loc += E_光追阶段ID::e_光追阶段ID_材质回调;
		scene->m_光追后端->m_线管创建配置信息.m_Paths[loc] = { path, E_着色阶段::e_光追回调 };
	}
	else {
		scene->m_光追后端->m_线管创建配置信息.m_Paths.push_back({path, E_着色阶段::e_光追回调 });
	}
	scene->m_光追后端->m_重新编译线管 = true;
}

void f_scene_删除光追材质着色器(S_Scene* scene, int32 loc) {
	if (loc >= 0 && loc + E_光追阶段ID::e_光追阶段ID_材质回调 < scene->m_光追后端->m_线管创建配置信息.m_Paths.size()) {
		scene->m_光追后端->m_线管创建配置信息.m_Paths.erase(scene->m_光追后端->m_线管创建配置信息.m_Paths.begin() + loc);
	}
	else {
		scene->m_光追后端->m_线管创建配置信息.m_Paths.pop_back();
	}
	scene->m_光追后端->m_重新编译线管 = true;
}

void f_scene_更新物体渲染(S_Scene* scene, S_物体* object) {
	scene->f_更新渲染物体();
}

void f_scene_设置光追渲染采样(S_Scene* scene, S_RayRenderAttr& Attr) {
	scene->m_光追后端->m_mapPtr_渲染帧数据->m_BouncesNum = Attr.反弹次数;
	scene->m_光追后端->m_mapPtr_渲染帧数据->m_SamplesNum = Attr.光采样;
}

//void f_scene_设置光追渲染子采样(S_Scene* scene, uint32 子采样) {
//}

void f_scene_设置环境着色器(S_Scene* scene, int32 环境着色器ID) {
	scene->m_光追后端->m_mapPtr_渲染帧数据->m_world_ShaderID = 环境着色器ID;
}

void f_scene_设置光追渲染器渲染(S_Scene* scene, bool 是否渲染) {
	scene->m_光追后端->m_渲染 = 是否渲染;
}

void f_scene_重载渲染器绑定纹理(S_Scene* scene, S_纹理* tex) {
	uint32 num = S_光追后端::G纹理.size();
	auto* data = S_光追后端::G纹理.data();

	//static std::mutex g分配内存锁;
	//g分配内存锁.lock();
	scene->m_光追后端->f_set纹理(tex);

	//for (uint32 i = 0; i < num; ++i) {
	//	if (data[i].m_Ptr == tex) {
	//		S_纹理* t = (S_纹理*)data[i].m_Ptr;
	//		scene->m_光追后端->f_set纹理(t, t->m_TexID);
	//		break;
	//	}
	//}
	//g分配内存锁.unlock();
}







void f_连接物理引擎车辆操控函数(S_Scene* scene, fun_物体async_p f) {
	scene->m_Physics.m_Bullet->mf_车辆操作更新 = f;
}

void f_连接渲染前更新函数(S_Scene* scene, fun_渲染前更新p f) {
	scene->mf_渲染前Update = f;
}

车辆容器_t f_get场景中车辆(S_Scene* scene) {
	车辆容器_t car = f_get车辆(&scene->m_Physics, E_物理引擎::E_物理引擎_Bullet);
	return car;
}








std::vector<S_物体*> f_scene_射线拾取(S_Scene* scene, const vec3& s, const vec3& e, float32* 最近坐标) {
	std::vector<S_物体*> pickObjs;
	

	float32 最小距离 = 100000000;
	vec3 交点 = {};
	vec3 dir = vec_normalize2(vec_sub(e, s));

	S_物体* 拾取物体 = nullptr;
	for (auto& obj : scene->m_物体set) {
		if (obj->m_可拾取) {
			S_物体拾取信息 物体拾取信息;
			switch (obj->m_Type) {
			case E_物体类型::t_包: 
			case E_物体类型::t_线段: {
				for (uint32 k = 0; k < obj->m_DataNum; ++k) {
					auto* m = f_ob_fromMesh(obj, k);
					物体拾取信息 = f_scene_射线拾取物体(obj, s, dir, m, nullptr);

					if (物体拾取信息.相交距离 < 最小距离) {
						最小距离 = 物体拾取信息.相交距离;
						拾取物体 = 物体拾取信息.物体;
					}
				}
			}
			default: {
				auto* m = f_ob_fromMesh(obj, 0);
				物体拾取信息 = f_scene_射线拾取物体(obj, s, dir, m, nullptr);

				if (物体拾取信息.相交距离 < 最小距离) {
					最小距离 = 物体拾取信息.相交距离;
					拾取物体 = 物体拾取信息.物体;
				}
			}
			}
		}
	}

	if (拾取物体) {
		pickObjs.push_back(拾取物体);

		if (最近坐标) {
			(*最近坐标) = 最小距离;
		}
	}
	
	//pickObjs = {};
	return pickObjs;
}

void f_scene_框选物体(S_物体* 待选物体, std::vector<S_物体*>& 选择物体, const vec4 平面[5]) {
	auto* ob_ptr = 待选物体->m_子物体.data();
	uint32 num = 待选物体->m_子物体.size();
	for (uint32 i = 0; i < num; ++i) {
		f_scene_框选物体(ob_ptr[i], 选择物体, 平面);
	}

	if(待选物体->m_是否实例) return;

	auto* me = f_ob_fromMesh(待选物体);
	if (me) {
		if (待选物体->m_根实例) {
			for (uint32 i = 0; i < num; ++i) {
				//auto mat = f_ob_get实例变换矩阵(待选物体, ob_ptr[i]->m_实例ID);
				auto mat = f_ob_get实例变换矩阵(待选物体, i);

				if (f_surface_框选包围盒(me, mat, 平面)) {
					bool 有拾取 = false;
					if (me->m_索引->m_Mem.m_大小) {
						有拾取 = f_surface_框选线框(me, mat, 平面);
					}
					else {
						有拾取 = f_surface_框选线段(me, mat, 平面);
					}

					if (有拾取) 选择物体.push_back(ob_ptr[i]);
				}
			}
		}
		else {
			auto mat = f_ob_get变换矩阵(待选物体);
			if (f_surface_框选包围盒(me, mat, 平面)) {
				bool 有拾取 = false;
				if (me->m_索引->m_Mem.m_大小) {
					有拾取 = f_surface_框选线框(me, mat, 平面);
				}
				else {
					有拾取 = f_surface_框选线段(me, mat, 平面);
				}
				if(有拾取) 选择物体.push_back(待选物体);
			}
		}
	}
	
	
	
}





S_实例拾取信息 f_scene_射线拾取物体实例(S_物体* obj, const vec3& s, const vec3& e) {
	S_实例拾取信息 物体实例ID = { -1, 100000000 };
	vec3 交点 = {};
	vec3 dir = vec_normalize2(vec_sub(e, s));

	auto* m = f_ob_fromMesh(obj);
	if (!m) return 物体实例ID;


	float32 当前距离 = 100000000;
	//Mat44f* mat_prt = f_buf_map板载缓存<Mat44f>(obj->m_变换矩阵);
	Mat44f* mat_prt = f_buf_Mat44_ptr(obj->m_变换矩阵);
	uint32 num = obj->m_变换矩阵.m_Mem.m_大小;

	switch (obj->m_Type) {
	case E_物体类型::t_骨骼:
	case E_物体类型::t_骨架: {
		if (obj->m_骨架) {
			float32* scale_prt = f_buf_F32_ptr(obj->m_骨架->m_骨骼长度);

			for (uint32 i = 0; i < num; ++i) {
				//mat = (float32*)&mat_prt[i];
				if (f_surface_射线面相交(m, mat_prt[i], scale_prt[i], s, dir, 交点, 当前距离)) {
					if (当前距离 <= 物体实例ID.相交距离) {
						物体实例ID.相交距离 = 当前距离;
						物体实例ID.实例ID = i;
					}
				}
			}
		}
		break;
	}

	default:
		for (uint32 i = 0; i < num; ++i) {
			//mat = (float32*)&mat_prt[i];
			if (f_surface_射线面相交(m, mat_prt[i], 1.0f, s, dir, 交点, 当前距离)) {
				if (当前距离 <= 物体实例ID.相交距离) {
					物体实例ID.相交距离 = 当前距离;
					物体实例ID.实例ID = i;
				}
			}
		}
		break;
	}

	//f_buf_unmap板载缓存(obj->m_变换矩阵);
	return 物体实例ID;
}




void f_scene_渲染器GPU内存映射() {
	//return;
	for (auto& e : g场景容器) {
		e.second->m_光追后端->f_mapGPU参数();
	}
}

void f_scene_渲染器GPU内存解映射() {
	//return;
	for (auto& e : g场景容器) {
		e.second->m_光追后端->f_unmapGPU参数();
	}
}

void f_scene_渲染器分配GPU内存() {
	//return;
	for (auto& e : g场景容器) {
		e.second->m_光追后端->f_resizeGPU参数();
	}
}

void f_scene_物体GPU内存分配() {

	/*if (S_物体::g_包围盒物体->m_变换矩阵->m_mappedPtr) {
		S_物体::g_包围盒物体->m_变换矩阵->f_unmap();
	}
	if (S_物体::g_包围盒物体->m_变换矩阵->m_分配器.maxNum) {
		f_buf_resize(S_物体::g_包围盒物体->m_变换矩阵, S_物体::g_包围盒物体->m_变换矩阵->m_分配器.maxNum);
	}
	S_物体::g_包围盒物体->m_变换矩阵->f_map();*/


}


uint32 f_scene_渲染器分配平行光内存(S_Scene* scene, uint32 num) {
	return f_buf_galloc(scene->m_光追后端->m_平行光缓冲区, num).m_偏移;
}

uint32 f_scene_渲染器分配点光源内存(S_Scene* scene, uint32 num) {
	return f_buf_galloc(scene->m_光追后端->m_点光源缓冲区, num).m_偏移;
}

uint32 f_scene_渲染器分配区域光内存(S_Scene* scene, uint32 num) {
	return f_buf_galloc(scene->m_光追后端->m_区域光缓冲区, num).m_偏移;
}

uint32 f_scene_渲染器分配聚光灯内存(S_Scene* scene, uint32 num) {
	return f_buf_galloc(scene->m_光追后端->m_聚光灯缓冲区, num).m_偏移;
}



void f_scene_渲染器设置平行光(S_Scene* scene, uint32 ID, const S_LigthParallel& ligth) {
	//ID = scene->m_光追后端->m_平行光缓冲区->m_分配器.m_内存分配偏移[ID].x;
	((S_LigthParallel*)scene->m_光追后端->m_平行光缓冲区->m_mappedPtr)[ID] = ligth;
}

void f_scene_渲染器设置平行光启用(S_Scene* scene, uint32 ID, const bool e) {
	//ID = scene->m_光追后端->m_平行光缓冲区->m_分配器.m_内存分配偏移[ID].x;
	((S_LigthParallel*)scene->m_光追后端->m_平行光缓冲区->m_mappedPtr)[ID].m_enable = e;
}

void f_scene_渲染器设置点光源(S_Scene* scene, uint32 ID, const S_LigthPoint& ligth) {
	//ID = scene->m_光追后端->m_点光源缓冲区->m_分配器.m_内存分配偏移[ID].x;
	//f_buf_T元素_at()
	((S_LigthPoint*)scene->m_光追后端->m_点光源缓冲区->m_mappedPtr)[ID] = ligth;
}

void f_scene_渲染器设置点光源启用(S_Scene* scene, uint32 ID, const bool e) {
	//ID = scene->m_光追后端->m_点光源缓冲区->m_分配器.m_内存分配偏移[ID].x;
	((S_LigthPoint*)scene->m_光追后端->m_点光源缓冲区->m_mappedPtr)[ID].m_enable = e;
}

void f_scene_渲染器设置聚光灯(S_Scene* scene, uint32 ID, const S_LigthSpot& ligth) {
	//ID = scene->m_光追后端->m_聚光灯缓冲区->m_分配器.m_内存分配偏移[ID].x;
	((S_LigthSpot*)scene->m_光追后端->m_聚光灯缓冲区->m_mappedPtr)[ID] = ligth;
}

void f_scene_渲染器设置聚光灯启用(S_Scene* scene, uint32 ID, const bool e) {
	//ID = scene->m_光追后端->m_聚光灯缓冲区->m_分配器.m_内存分配偏移[ID].x;
	((S_LigthSpot*)scene->m_光追后端->m_聚光灯缓冲区->m_mappedPtr)[ID].m_enable = e;


}

void f_scene_渲染器设置区域光(S_Scene* scene, uint32 ID, const S_LigthArea& ligth) {
	//ID = scene->m_光追后端->m_区域光缓冲区->m_分配器.m_内存分配偏移[ID].x;
	((S_LigthArea*)scene->m_光追后端->m_区域光缓冲区->m_mappedPtr)[ID] = ligth;
}

void f_scene_渲染器设置区域光启用(S_Scene* scene, uint32 ID, const bool e) {
	//ID = scene->m_光追后端->m_区域光缓冲区->m_分配器.m_内存分配偏移[ID].x;
	((S_LigthArea*)scene->m_光追后端->m_区域光缓冲区->m_mappedPtr)[ID].m_enable = e;
}

void f_render_刷新渲染(S_Scene* scene) {
	scene->m_光追后端->f_重置渲染帧记录();
}










